<?php
// +--------------------------------------------------------------------------------------------
// | Author: Admin <543423@qq.com>
// +--------------------------------------------------------------------------------------------
// | Copyright ThinkAdmin http://www.thinkadmin.cn All rights reserved.
// +--------------------------------------------------------------------------------------------

/**
 * +--------------------------------------------------------------------------------------------
 * |扩展控制
 * +--------------------------------------------------------------------------------------------
 * |扩展管理
 * +--------------------------------------------------------------------------------------------
 */
namespace Admin\Controller;
class ExtensionController extends AdminController{

	/**
	 *
	 * 插件管理
	 */
	public function plugin(){
		$this->listPluginTool('Plugin');
	}

	/**
	 *
	 * 工具管理
	 */
	public function tool(){
		$this->listPluginTool('Tool');
	}

	/**
	 *
	 *列出插件或者工具列表
	 * @param $type 类型，Plugin 插件  Tool工具
	 */
	private function listPluginTool($type = ''){
		$path = scandir(NOWEB_PATH.'./Write/'.$type);
		$config = array();
		foreach ($path as $k=>$v){
			if($v !== '.' && $v !== '..'){ //只获取目录
				$config = HOOK($type, $v, '', '', 'getConfig');
				$config['MARK'] = $v;
				$list[] = $config;
			}
		}
		$this->assign('list',$list);
		$tistiem = time();
		session('token',$tistiem); //SESSION保存上传令牌
		$this->assign('token', get_md5_rules($tistiem));
		$this->assign('type', $type);
		$this->display('listplugintool');
	}

	/**
	 * 启用插件
	 * @remark 插件启用
	 */
	public function pluginuse(){
		!IS_POST && $this->_empty(); //只接收POST数据
		$this->statusPluginTool('Plugin', I('post.plugin'),2);
	}

	/**
	 * 插件停用
	 * @remark 插件停用
	 */
	public function pluginstop(){
		!IS_POST && $this->_empty(); //只接收POST数据
		$this->statusPluginTool('Plugin', I('post.plugin'),1);
	}


	/**
	 *
	 * 插件和钩子状态改变
	 * @param $type  类型，Plugin 插件  Tool工具
	 * @param $name 名称
	 * @param $status 状态值
	 */
	private function statusPluginTool($type, $name, $status){
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取对象
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		if($config['STATUS'] === 0) $this->msg(0, L('NO_INSTALL_TIP'));//还未安装
		if($config['STATUS'] === $status) $status ==1 ? $this->msg(0, L('Y_STOPUSE')) :  $this->msg(0, L('Y_USE'));   //已启用
		$mystatus = $status==1 ?  0 : 1;

		//菜单处理
		if(isset($config['MENUID']) && !empty($config['MENUID'])){
			$menu = new \Common\Model\SystemMenuModel;
			$menu->deleteCache(); //清空菜单缓存
			$menu->startTrans(); //启动事务
			$menudata = implode(',', $config['MENUID']);
			$menu->where(array('id'=>array('in',$menudata)))->save(array('status'=>$mystatus)); //菜单
		}

		//权限处理
		if(isset($config['RULESID']) && !empty($config['RULESID'])){
			$rules = new \Common\Model\SystemRulesModel;
			$rules->deleteCache(); //清空缓存
			$rules->startTrans(); //启动事务
			$rulesdata = implode(',', $config['RULESID']);
			$rules->where(array('id'=>array('in',$rulesdata)))->save(array('status'=>$mystatus)); //权限
		}

		$config['STATUS'] =$status;
		$msg = \Common\Lib\FileUtil::insertFile("<?php \nreturn ".var_export($config,true)." \n?>", $obj->path.'./Config/config.php');
		if($msg[0]){  //文件保存成功
			isset($menu) && $menu->commit();  //事务提交
			isset($rules) && $rules->commit();  //事务提交
		}else{
			isset($menu) && $menu->rollback();  //事务回滚
			isset($rules) && $rules->rollback();  //事务回滚
		}
		$this->msg($msg[0],$msg[1]);
	}

	/**
	 * 安装插件
	 * @remark 安装插件
	 */
	public function inplugin(){
		!IS_POST && $this->_empty();  //只能post请求
		$this->installPluginTool('Plugin',  I('post.plugin'));
	}

	/**
	 *
	 * 安装插件或者工具处理方法
	 * @param $type 类型，Plugin 插件  Tool工具
	 * @param $name 名称
	 */
	private function installPluginTool($type, $name){
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取对象
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		if($config['STATUS'] !== 0) $this->msg(0,L('STATUS_NO_UNINSTALL'));

		//菜单处理
		if(isset($config['MENUSQL']) && !empty($config['MENUSQL'])){
			$config['MENUID'] = array(); //设置菜单数组
			//把所有写入菜单状态改变为停用
			foreach ($config['MENUSQL'] as $k=>$v){
				isset($config['MENUSQL'][$k]['status']) && $config['MENUSQL'][$k]['status'] = 0;  //设置菜单状态为未使用
			}
			//写入菜单
			$menu = new \Common\Model\SystemMenuModel;
			$menu->startTrans(); //启动事务
			$addmenuallid = array();
			foreach ($config['MENUSQL'] as $k=>$v){
				$pidname = 'pid'.$k;
				$data = $v;
				if(!is_int($data['pid'])){  //所属父级处理
					$varpidname = explode('key', $data['pid']);
					$varpidname = 'pid'.$varpidname[1];
					if(isset($$varpidname)) $data['pid'] = $$varpidname;
				}
				$$pidname = $menu->add($data);
				if(!$$pidname){
					$menu->rollback(); //事务回滚
					$this->msg(0, L('MEUN_INSERT_ERROR'));
				}else{
					$addmenuallid[] = $$pidname;
				}
			}
			$config['MENUID'] =$addmenuallid;
		}

		//权限处理
		if(isset($config['RULESSQL']) && !empty($config['RULESSQL'])){
			$config['RULESID'] = array();//清除已有权限id
			//把所有写入权限规则状态改变为停用
			foreach ($config['RULESSQL'] as $k=>$v){
				isset($config['RULESSQL'][$k]['status']) && $config['RULESSQL'][$k]['status'] = 0;//设置权限状态为未使用
			}
			$rules = new \Common\Model\SystemRulesModel;
			$rules->startTrans(); //启动事务
			$addrulesallid = array();
			foreach ($config['RULESSQL'] as $k=>$v){
				$pidname = 'pid'.$k;
				$data = $v;
				if(!is_int($data['pid'])){  //所属父级处理
					$varpidname = explode('key', $data['pid']);
					$varpidname = 'pid'.$varpidname[1];
					if(isset($$varpidname)) $data['pid'] = $$varpidname;
				}
				$$pidname = $rules->add($data);
				if(!$$pidname){
					isset($menu) && $menu->rollback(); //事务回滚
					$rules->rollback(); //事务回滚
					$this->msg(0, L('RULES_INSERT_ERROR'));
				}else{
					$addrulesallid[] = $$pidname;
				}
			}
			$config['RULESID'] =$addrulesallid;
		}

		//执行额外sql文件
		if(isset($config['SQLLIST']) && !empty($config['SQLLIST']) && is_array($config['SQLLIST'])){
			foreach ($config['SQLLIST'] as $k=>$v){
				$sqlPath = $obj->path.'/Sql/'.$v;
				if(file_exists($sqlPath)){
					$con = file_get_contents($sqlPath);//读取文件内容
					if(!empty($con)){
						if(M()->execute($con) === false){
							isset($menu) && $menu->rollback(); //事务回滚
							isset($rules) && $rules->rollback(); //事务回滚
							$this->msg(0,L('IMPORT_SQLFILE_ERROR').$v);
						}
					}
				}
			}
		}

		$config['STATUS'] = 1;  //标记安装配置
		$msg = \Common\Lib\FileUtil::insertFile("<?php \nreturn ".var_export($config,true)." \n?>", $obj->path.'./Config/config.php');
		if($msg[0]){  //文件保存成功
			isset($menu) && $menu->commit();  //事务提交
			isset($rules) && $rules->commit();  //事务提交
			//更新角色缓存
			$role =new \Common\Model\SystemRoleModel;
			$role->deleteCache();
			$this->msg(1,L('OPERATION_SUCCESS'));
		}else{
			//开始删除数据表
			foreach ($config['TABEL'] as $k=>$v){
				$sql = 'DROP TABLE `'.C('DB_PREFIX').$v.'`;';  //循环删除表
				M()->query($sql);
			}
			isset($menu) && $menu->rollback(); //事务回滚
			isset($rules) && $rules->rollback(); //事务回滚
			$this->msg(0,L('OPERATION_ERROR'));
		}
	}

	/**
	 * 卸载插件
	 * @remark 卸载插件
	 */
	public function unplugin(){
		!IS_POST && $this->_empty();  //只能post请求
		$this->unPluginTool('Plugin',  I('post.plugin'));
	}

	/**
	 *
	 * 卸载插件或者工具方法
	 * @param $type 类型，Plugin 插件  Tool工具
	 * @param $name 名称
	 */
	private function unPluginTool($type, $name){
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取对象
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		if($config['STATUS'] === 0) $this->msg(0, L('STATUS_INUNINSTALL'));
		if($config['STATUS'] !== 1) $this->msg(0, L('PLEASE_STOP_USE'));

		//如果存在菜单开始删除菜单
		if(isset($config['MENUID']) && !empty($config['MENUID'])){

			$menu = new \Common\Model\SystemMenuModel;
			$menu->startTrans(); //启动事务
			$menudata = implode(',', $config['MENUID']);
			$menu->deleteCache($menudata); //删除菜单缓存
			if($menu->where(array('id'=>array('in',$menudata)))->count()){ //存在菜单
				if($menu->where(array('id'=>array('in',$menudata)))->delete() === false){ //菜单删除失败
					$menu->rollback();  //菜单事务回滚
					$this->msg(0, L('MENU_DELETE_ERROR'));
				}
			}
			$config['MENUID'] = array();

			//删除用户包含的快捷菜单，避免垃圾
			$mSystemUserShortcutmenu = M('SystemUserShortcutmenu');
			$mSystemUserShortcutmenu->startTrans(); //启动事务
			$mSystemUserShortcutmenu->where(array('menu_id'=>array('in',$menudata)))->delete();

		}

		//如果存在权限规则开始删除权限规则
		if(isset($config['RULESID']) && !empty($config['RULESID'])){
			$rules = new \Common\Model\SystemRulesModel;
			$rules->startTrans(); //启动事务
			$rulesdata = implode(',', $config['RULESID']);
			$rules->deleteCache($rulesdata); //删除权限缓存
			if($rules->where(array('id'=>array('in',$rulesdata)))->count()){ //存在规则
				if($rules->where(array('id'=>array('in',$rulesdata)))->delete() === false){ //权限删除失败
					isset($menu) && $menu->rollback();  //菜单事务回滚
					$rules->rollback();  //菜单事务回滚
					$this->msg(0,L('RULES_DELETE_ERROR'));
				}
			}
			$config['RULESID'] = array();
			//删除角色拥有的权限规则，避免垃圾
			$mSystemRoleRules = M('SystemRoleRules');
			$mSystemRoleRules->startTrans(); //启动事务
			$mSystemRoleRules->where(array('rules_id'=>array('in',$rulesdata)))->delete();

		}

		//如果存在数据表开始删除数据表
		if(isset($config['TABEL']) && !empty($config['TABEL'])){
			foreach ($config['TABEL'] as $k=>$v){
				if(M()->query("SHOW TABLES LIKE  '".C('DB_PREFIX').$v."'")){
					$sql = 'DROP TABLE `'.C('DB_PREFIX').$v.'`;';  //循环删除表
					if(M()->query($sql) ===false){
						isset($menu) && $menu->rollback();  //菜单事务回滚
						isset($rules) && $rules->rollback();  //菜单事务回滚
						isset($mSystemUserShortcutmenu) && $mSystemUserShortcutmenu->rollback();
						isset($mSystemRoleRules) && $mSystemRoleRules->rollback();
						$this->msg(0, L('DELETE_TABEL_ERROR'));
					}
				}
			}
		}
		$config['STATUS'] = 0;  //标记配置
		//开始写入配置
		$config['STATUS'] = 0;
		$msg = \Common\Lib\FileUtil::insertFile("<?php \nreturn ".var_export($config,true)." \n?>", $obj->path.'./Config/config.php');
		if($msg[0]){  //文件保存成功
			isset($menu) && $menu->commit();  //事务提交
			isset($rules) && $rules->commit();  //事务提交
			isset($mSystemUserShortcutmenu) && $mSystemUserShortcutmenu->commit();
			isset($mSystemRoleRules) && $mSystemRoleRules->commit();
		}else{
			isset($mSystemUserShortcutmenu) && $mSystemUserShortcutmenu->rollback();
			isset($mSystemRoleRules) && $mSystemRoleRules->rollback();
			isset($menu) && $menu->rollback();  //事务回滚
			isset($rules) && $rules->rollback();  //事务回滚
		}
		$this->msg($msg[0],$msg[1]);
	}

	/**
	 * 下载插件
	 * @remark 插件下载,打包下载插件
	 */
	public function downplugin(){
		$this->downPluginTool('Plugin',  I('get.plugin'));
	}

	/**
	 *
	 * 下载插件或者工具方法
	 * @param $type 类型，Plugin 插件  Tool工具
	 * @param $name 名称
	 */
	private function downPluginTool($type, $name){
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取对象
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		//开始打包
		$tempath = RUNTIME_PATH.'Mytemp/'.(microtime(TRUE)*10000).rand(1,10000).'/'; //定义一个临时目录
		!file_exists($tempath) && \Common\Lib\FileUtil::createDir($tempath); //临时目录不存在开始创建临时目录
		$filename = $tempath.'/'.$name.'.zip'; //定义文件路径
		$pathZip = $filename;
		$archive = new \Common\Lib\PclZip;
		$archive->PclZip($pathZip);
		$v_list = $archive->create($obj->path,PCLZIP_OPT_REMOVE_PATH, NOWEB_PATH."Write/$type/");
		if(!file_exists($filename)) $this->msg(0,L('PCLZIP_DB_ERROR')); //打包后如果压缩包还不存在那么说明打包失败
		//文件的类型
		header('Content-type: application/octet-stream');
		//下载显示的名字
		header('Content-Disposition: attachment; filename='.$name.'.zip');
		readfile($filename);
		exit();
	}

	/**
	 * 删除插件
	 * @remark 删除插件
	 */
	public function delplugin(){
		!IS_POST && $this->_empty(); //只支持post请求
		$this->delPluginTool('Plugin', I('post.plugin'));
	}


	/**
	 *
	 * 删除插件或者工具方法
	 * @param $type 类型，Plugin 插件  Tool工具
	 * @param $name 名称
	 */
	private function delPluginTool($type, $name){
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取对象
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		if($config['STATUS'] !== 0) $this->msg(0, L('P_UNINSTALL_DELETE'));
		//开始删除目录
		\Common\Lib\FileUtil::unlinkDir($obj->path) ? $this->msg(1,L('DELETE_SUCCESS')) : $this->msg(0,L('DELETE_ERROR'));
	}

	/**
	 * 上传插件
	 * @remark 上传插件
	 */
	public function upplugin(){
		!IS_POST && $this->_empty(); //只接收POST数据
		$msg = $this->_zipupload('Plugin');
		$this->msg($msg[0],$msg[1]);
	}

	/**
	 *
	 * 包上传公共处理方法
	 * @param $type 类型 是插件或者工具或者其他类型判断
	 */
	private function _zipupload($type = ''){
		if(!C('UPLOAD.IS_UPLOAD')) return array(0,L('STSTEM_NO_UPLOAD')); //系统关闭文件上传
		if (empty($_FILES) || !isset($_FILES['Filedata'])) return array(0,L('NO_UPLOAD_FILE')); //没有上传文件
		//获取系统上传限制
		$getSysInfo = get_sysinfo();
		$systemUploadSize = (int)$getSysInfo['fileupload'];
		$sizeType = preg_replace('/[^a-zA-Z]/', '', $getSysInfo['fileupload']);
		switch (strtolower($sizeType)){
			case 'k':
				$systemUploadSize = $systemUploadSize*1024;
				break;
			case 'm':
				$systemUploadSize = $systemUploadSize*1024*1024;
				break;
			case 'g':
				$systemUploadSize = $systemUploadSize*1024*1024*1024;
				break;
		}
		if((int)$_FILES['Filedata']['size'] > $systemUploadSize || (int)$_FILES['Filedata']['size'] > C('UPLOAD.SIZE'))  return array(0,L('FILE_CG_SYSTEM_XZ')); //大小检查
		//文件类型检查
		if(get_file_type($_FILES['Filedata']['tmp_name']) !=='zip') return array(0, L('UPLOAD_TYPE_ERROR')); //上传文件类型错误

		//开始解压到临时目录
		$tempath = RUNTIME_PATH.'Mytemp/'.(microtime(TRUE)*10000).rand(1,10000).'/'; //定义临时目录
		!file_exists($tempath) && \Common\Lib\FileUtil::createDir($tempath); //临时目录不存在开始创建临时目录
		$archive = new \Common\Lib\PclZip;
		$archive->PclZip($_FILES['Filedata']['tmp_name']);
		if(!$archive->extract(PCLZIP_OPT_PATH, $tempath)){
			\Common\Lib\FileUtil::unlinkDir($tempath); //删除临时目录
			return array(0,"Error : ".$archive->errorInfo(true));
		}

		//扫描目录
		$dir = scandir($tempath);
		$name = '';
		foreach ($dir as $k=>$v){
			if($v !== '.' && $v !== '..'){
				$name = $v;
				break;
			}
		}

		if(empty($name)){  //没有找到为空
			\Common\Lib\FileUtil::unlinkDir($tempath); //删除临时目录
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		//包配置文件检查
		$filename = $tempath.$name.'/Config/config.php';  //配置文件路径
		if(!file_exists($filename)){
			\Common\Lib\FileUtil::unlinkDir($tempath); //删除临时目录
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		$config = include $filename;

		if(!is_array($config)) { //配置必须是数组
			\Common\Lib\FileUtil::unlinkDir($tempath); //删除临时目录
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		if(!isset($config['STATUS'])) { //配置必须包含状态
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		if(!isset($config['TYPE'])){	//必须包含类型
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		//检查包是否已经被使用或存在
		if(file_exists(NOWEB_PATH."Write/$type/".$name.'/')) {//标识重复
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0, L('MARK_CF'));
		}

		if(!isset($config['NAME'])){//必须包含名称
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		if(strtolower($config['TYPE']) !==strtolower($type)){//类型比对不一致
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0,L('UPLOAD_FORMAT_BAO_ERROR'));
		}

		//移动目录
		if(!rename($tempath.$name.'/', NOWEB_PATH."Write/$type/".$name.'/')){
			\Common\Lib\FileUtil::unlinkDir($tempath);
			return array(0, L('YD_ERROR'));
		}
		//上传成功
		\Common\Lib\FileUtil::unlinkDir($tempath);
		return array(1,L('UPLOAD_SUCCESS'));
	}

	/**
	 * 外部使用钩子
	 * @remark 外部使用钩子
	 */
	public function usehook(){
		$type =ucfirst(strtolower(I('get.type')));
		$name = ucfirst(strtolower(I('get.name')));
		$hookc = ucfirst(strtolower(I('get.hookc','Run')));
		$hooka = strtolower(I('get.hooka'));
		if(empty($type) || empty($name) || empty($hookc) || empty($hooka)) $this->_empty(); //参数检查
		$obj = HOOK($type, $name, '', '', 'getClass'); //获取插件
		if($obj === null) $this->_empty();
		if(!$config =$obj->configs) $this->_empty(); //获取配置出错
		if($config['STATUS'] === 0) $this->msg(0,  L('NO_INSTALL_TIP'));
		//权限检测
		if(C('IS_AUTH') && !RULES_AUTH(array(MODULE_NAME.'/'.CONTROLLER_NAME.'/'.ACTION_NAME.'/'.$type.'/'.$name.'/'.$hookc.'/'.$hooka))){  //权限检查
			$this->msg(0,L('NO_ACCESS')); //没有权限
		}
		if($config['STATUS'] !== 2)$this->msg(0,L('PLUGIN_NO_USE')); //插件未使用
		if(!method_exists($obj, $hooka)){
			$this->_empty();
		}
		HOOK($type,$name,$hooka,$hookc,'call',null,true,true); //IP黑名单钩子检测IP黑名单
	}

}
